#include "Configuraciones.h"
#include <stdexcept>
#include <fstream>

Configuraciones::Configuraciones(const std::string& nomArch, bool crear):
_nomArchXML(nomArch) {
	TiXmlDocument xml;
	bool result = false;
	// Chequea si existe o hay que crear un archivo de configuraciones
	if (crear || !existeArch(nomArch)) {
		// Crea un archivo con valores por defecto y lo guarda
		crearConfigXML(xml);
		result = true;
	}
	else {
		// Carga el archivo a parsear
		result = xml.LoadFile(nomArch.c_str());

		// Obtiene el "root node"
		TiXmlElement* root = xml.FirstChildElement();

		// Carga los usuarios en el mapa
		TiXmlElement* nodo = root->FirstChildElement("usuarios");
		procesarListado(mapUsuarios, nodo, "credencial", "usuario", "clave");

		// Carga los tipos MIME estaticos en el mapa
		nodo = root->FirstChildElement("tipoEstatico");
		procesarListado(mapTiposEstaticos, nodo, "tipo", "extension", "contentType");

		// Carga los tipos MIME dinamicos en el mapa
		nodo = root->FirstChildElement("tipoDinamico");
		procesarListado(mapTiposDinamicos, nodo, "tipo", "extension", "comando");

		// Carga los codigos de errores en el mapa
		nodo = root->FirstChildElement("pagErrores");
		procesarListado(mapPaginasErrores, nodo, "tipo", "codigo", "pagina");

		// Carga las configuraciones basicas
		nodo = root->FirstChildElement("basica");
		cargarConfiguracionBasica(nodo);
	}

	// Guarda el nombre del archivo de configuraciones
	if (!result)
		throw std::runtime_error("Error al parsear el XML de configuraciones");
}

Configuraciones::~Configuraciones() {
	guardar();
}

void Configuraciones::procesarListado(stringmap& mapa, TiXmlElement* nodo,
		const std::string& fila, const std::string& key, const std::string& value) {
	std::string clave;
	std::string valor;
	TiXmlHandle h(nodo);
	TiXmlElement* aux = h.FirstChild(fila.c_str()).ToElement();
	while (aux != NULL) {
		// Extrae los valores
		clave = GetField(aux, key.c_str());
		valor = GetField(aux, value.c_str());

		// Los agrega al mapa
		mapa[clave] = valor;
		aux = aux->NextSiblingElement(fila.c_str());
	}
}

void Configuraciones::cargarConfiguracionBasica(TiXmlElement* nodo) {
	// Procesa uno de los tags a la vez
	baseDir = GetField(nodo, "direcBase");
	nroPuerto = GetField<unsigned short int>(nodo, "nroPuerto");
	autenticacionBasica = GetField<bool>(nodo, "authBasica");
	timeout = GetField<unsigned short int>(nodo, "timeout");
	nroPuertoCtrl = GetField<unsigned short int>(nodo, "nroPuertoControl");
	maxConexionesCliente = GetField<unsigned short int>(nodo, "connMaxCliente");
	maxConexionesSimultaneas = GetField<unsigned short int>(nodo, "connMaxSimultaneas");
}

TiXmlElement* Configuraciones::guardarConfiguracionBasica() const {
	TiXmlElement* basica = new TiXmlElement("basica");

	TiXmlElement* root = NewField("direcBase", baseDir.c_str());
	TiXmlElement* pTimeout = NewField<unsigned short int>("timeout", timeout);
	TiXmlElement* puerto = NewField<unsigned short int>("nroPuerto", nroPuerto);
	TiXmlElement* prtoCtrl = NewField<unsigned short int>("nroPuertoControl", nroPuertoCtrl);
	TiXmlElement* autent = NewField<bool>("authBasica", autenticacionBasica);
	TiXmlElement* maxCC = NewField<unsigned short int>("connMaxCliente", maxConexionesCliente);
	TiXmlElement* maxSim = NewField<unsigned short int>("connMaxSimultaneas", maxConexionesSimultaneas);

	basica->LinkEndChild(root);
	basica->LinkEndChild(puerto);
	basica->LinkEndChild(pTimeout);
	basica->LinkEndChild(prtoCtrl);
	basica->LinkEndChild(autent);
	basica->LinkEndChild(maxCC);
	basica->LinkEndChild(maxSim);
	return basica;
}

void Configuraciones::guardar() {
	// Carga todos los datos en un nuevo XML, lo arma por secciones
	// Configuracion basica
	TiXmlElement* basica = guardarConfiguracionBasica();

	// Listados, carga los mapas en el nodo
	TiXmlElement* errores = buildListado(mapPaginasErrores, "pagErrores",
			"tipo", "codigo", "pagina");
	TiXmlElement* usuarios = buildListado(this->mapUsuarios, "usuarios",
			"credencial", "usuario", "clave");
	TiXmlElement* dinamico = buildListado(this->mapTiposDinamicos,
			"tipoDinamico", "tipo", "extension", "comando");
	TiXmlElement* estatico = buildListado(this->mapTiposEstaticos,
			"tipoEstatico", "tipo", "extension", "contentType");

	// El elemento raiz del XML
	TiXmlElement raiz("configuraciones");
	raiz.LinkEndChild(basica);
	raiz.LinkEndChild(estatico);
	raiz.LinkEndChild(dinamico);
	raiz.LinkEndChild(errores);
	raiz.LinkEndChild(usuarios);

	// El documento y su declaracion
	TiXmlDocument xml;

	xml.InsertEndChild(raiz);

	// Sobre-escribe el xml que ya existe
	xml.SaveFile(_nomArchXML.c_str());
}

TiXmlElement* Configuraciones::buildListado(const stringmap& mapa, const std::string& grupo,
		const std::string& fila, const std::string& key, const std::string& value) {
	// El nodo que contiene a todo el resto
	TiXmlElement* grupoNodo = new TiXmlElement(grupo.c_str());

	// Itera por todos los elementos del mapa
	TiXmlElement* filaDatos;
	stringmap::const_iterator mi;
	for (mi = mapa.begin(); mi != mapa.end(); mi++) {
		// Los agrega a la fila y esta al nodo principal
		filaDatos = new TiXmlElement(fila.c_str());
		filaDatos->LinkEndChild(NewField(key.c_str(), mi->first.c_str()));
		filaDatos->LinkEndChild(NewField(value.c_str(), mi->second.c_str()));
		grupoNodo->LinkEndChild(filaDatos);
	}

	// Devuelve el grupo con todos los nodos dentro
	return grupoNodo;
}

bool Configuraciones::existeArch(const std::string& nomArch) {
	// Trata de abrir el archivo, si lo logro existe
	std::fstream f(nomArch.c_str());
	bool existeArch = f.good();
	return existeArch;
}

void Configuraciones::crearConfigXML(TiXmlDocument& xml) {
	nroPuerto = 4000;
	nroPuertoCtrl = 33000;
	timeout = 30;
	maxConexionesSimultaneas = 100;
	maxConexionesCliente = 2;
	baseDir = "/www/root";
	autenticacionBasica = false;

	// Tipos MIME estaticos
	mapTiposEstaticos["html"] = "text/html";
	mapTiposEstaticos["gif"] = "image/gif";
	mapTiposEstaticos["jpg"] = "image/jpg";
	mapTiposEstaticos["jpeg"] = "image/jpeg";
	mapTiposEstaticos["css"] = "text/css";
	mapTiposEstaticos["js"] = "text/javascript";
	mapTiposEstaticos["ico"] = "image/ico";

	// Tipos MIME dinamicos
	mapTiposDinamicos["php"] = "/usr/bin/php-cgi {NAME}";
	mapTiposDinamicos["cgi"] = "{NAME}";

	// Paginas de errores
	mapPaginasErrores["400"] = "/error/400.html";
	mapPaginasErrores["401"] = "/error/401.html";
	mapPaginasErrores["403"] = "/error/403.html";
	mapPaginasErrores["404"] = "/error/404.html";
	mapPaginasErrores["500"] = "/error/500.html";
	mapPaginasErrores["501"] = "/error/501.html";

	// Usuarios
	mapUsuarios["pepe"] = "1234";
}
